package com.md.client.helper;

import com.md.base.BaseFilter;
import com.md.client.OpenApiClient;
import com.md.client.OpenLogsClient;
import com.md.config.LogClientConfiguration;
import com.md.context.LogServerContext;
import com.md.request.OperationLogsRequest;
import com.md.response.ApiResponse;
import com.md.util.HttpUtil;
import com.md.util.ObjectUtil;
import com.md.util.ThreadUtil;
import com.md.value.constants.CodeConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.concurrent.Executor;

import static com.md.value.LogsConstants.CREATE_LOG_THREAD_NAME;
import static com.md.value.LogsConstants.SERVER_CONTEXT_KEY_DATA_ID;
import static com.md.value.LogsConstants.SERVER_CONTEXT_KEY_IP_ADDRESS;

/**
 * @author 刘骄阳
 * 2024-10-16 15:26
 */
@Slf4j
@Component
public class OpenLogsFilter extends BaseFilter {
  private final LogClientConfiguration logClientConfiguration;
  private final Executor executor = ThreadUtil.newInstanceExecutor(1, CREATE_LOG_THREAD_NAME);

  public OpenLogsFilter(final LogClientConfiguration macClientConfiguration) {
    super(0);
    this.logClientConfiguration = macClientConfiguration;
  }

  @Override
  protected void doFilterInternal(@NonNull final HttpServletRequest request,
                                  @NonNull final HttpServletResponse response,
                                  @NonNull final FilterChain filterChain) throws IOException, ServletException {
    BodyCachingRequestWrapper wrappedRequest = new BodyCachingRequestWrapper(request);
    BodyCachingResponseWrapper responseWrapper = new BodyCachingResponseWrapper(response);
    String noLogRequired = logClientConfiguration.getPermitAllAntMatchers().stream().filter(noLogUrl ->
      request.getRequestURI().startsWith(noLogUrl)).findFirst().orElse(null);
    if (ObjectUtil.notEmpty(noLogRequired)) {
      filterChain.doFilter(wrappedRequest, response);
      return;
    }
    OperationLogsRequest operationLogsRequest = buildRequest(wrappedRequest);
    LocalDateTime startDate = LocalDateTime.now();
    try {
      LogServerContext.put(SERVER_CONTEXT_KEY_DATA_ID, operationLogsRequest.getDataId());
      LogServerContext.put(SERVER_CONTEXT_KEY_IP_ADDRESS, operationLogsRequest.getIpAddress());
      ThreadUtil.runAsync(() -> OpenLogsClient.recordOperatorLog(operationLogsRequest), "新增日志", executor);
      filterChain.doFilter(wrappedRequest, responseWrapper);
      if (ObjectUtil.unEquals(responseWrapper.getStatus(), CodeConstants.SUCCESS_CODE)) {
        operationLogsRequest.setErrorMsg(new String(responseWrapper.getCapturedData(), StandardCharsets.UTF_8));
      }
    } catch (Exception e) {
      log.error("日志处理发生异常: {}", e.getMessage(), e);
      operationLogsRequest.setErrorMsg(e.getMessage());
      errorEnum(responseWrapper, e);
    } finally {
      LocalDateTime endDate = LocalDateTime.now();
      operationLogsRequest.setConsumingTime(Duration.between(startDate, endDate).toMillis());
      ThreadUtil.runAsync(() -> OpenLogsClient.updateOperatorLog(operationLogsRequest), "更新日志", executor);
      LogServerContext.clear();
      responseWrapper.resetResponse(response);
    }
  }

  private String getDataId() {
    // todo 优化生成策略
    return RandomStringUtils.randomAlphanumeric(64);
  }

  private String getParentId(final HttpServletRequest request) {
    String parentId = request.getHeader(SERVER_CONTEXT_KEY_DATA_ID);
    if (ObjectUtil.isEmpty(parentId)) {
      return null;
    }
    return parentId;
  }

  private String getIP(final HttpServletRequest request) {
    return Optional.ofNullable(request.getHeader(SERVER_CONTEXT_KEY_IP_ADDRESS))
      .orElse(HttpUtil.getIpAddress(request));
  }

  private OperationLogsRequest buildRequest(final HttpServletRequest request) {
    OperationLogsRequest saveRequest = new OperationLogsRequest();
    saveRequest.setOperationContent("");
    saveRequest.setOperateDate(LocalDateTime.now());
    saveRequest.setConsumingTime(0L);
    Optional.ofNullable(getParentId(request)).ifPresent(saveRequest::setParentId);
    saveRequest.setIpAddress(getIP(request));
    saveRequest.setDataId(getDataId());
    // 获取请求参数
    saveRequest.setRequestParam(getRequestParam(request));
    saveRequest.setUrl(request.getRequestURI());
    ApiResponse apiResponse = OpenApiClient.queryApi(request.getRequestURI(), request.getMethod());
    saveRequest.setTenantId(apiResponse.getTenantId());
    saveRequest.setBrandId(apiResponse.getBrandId());
    saveRequest.setTitle(apiResponse.getTitle());
    saveRequest.setOperation(apiResponse.getOperation());
    return saveRequest;
  }

  private String getRequestParam(final HttpServletRequest request) {
    try {
      // 如果是文件类型，则不读取
      if (isFileUploadRequest(request)) {
        return "{}";
      }
      InputStream inputStream = request.getInputStream();
      StringBuilder stringBuilder = new StringBuilder();
      BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
      String line;
      while ((line = reader.readLine()) != null) {
        stringBuilder.append(line);
      }
      return stringBuilder.toString();
    } catch (IOException e) {
      log.error("解析请求体失败：{}", e.getMessage(), e);
    }
    return "{}";
  }

  private boolean isFileUploadRequest(final HttpServletRequest request) {
    String contentType = request.getContentType();
    return contentType != null && contentType.startsWith("multipart/");
  }

}
